package com.jintian.smart.kernel.flowable.service;

import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jintian.smart.kernel.common.util.StringUtil;
import com.jintian.smart.kernel.core.domain.PageQuery;
import com.jintian.smart.kernel.flowable.common.utils.BeanUtils;
import com.jintian.smart.kernel.flowable.constant.ProcessConstant;
import com.jintian.smart.kernel.flowable.domain.entity.BizFlow;
import com.jintian.smart.kernel.flowable.domain.entity.FlowAct;
import com.jintian.smart.kernel.flowable.domain.query.DeployVoQuery;
import com.jintian.smart.kernel.flowable.domain.view.DeployVo;
import com.jintian.smart.kernel.flowable.domain.view.DeploymentVo;
import com.jintian.smart.kernel.flowable.util.ProcessUtil;
import lombok.Builder;
import org.apache.commons.io.IOUtils;
import org.beetl.sql.core.page.PageResult;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Builder
@Service
public class DeployService {
    private final RepositoryService repositoryService;
    private final BizFlowService bizFlowService;
    private final FlowActService flowActService;

    public DeploymentVo deployByInputStream(MultipartFile file) throws IOException, XMLStreamException {
        byte[] bytes = IOUtils.toByteArray(file.getInputStream());
        Deployment deployment = null;
        if (file.getOriginalFilename().toLowerCase().endsWith(".json")) {
            JsonNode json = new ObjectMapper().readTree(bytes);
            BpmnModel bpmn = new BpmnJsonConverter().convertToBpmnModel(json);
            deployment = repositoryService.createDeployment().addBpmnModel(file.getOriginalFilename(), bpmn).deploy();
        } else if (file.getOriginalFilename().toLowerCase().endsWith(".xml")) {
            XMLInputFactory factory = XMLInputFactory.newInstance();
            XMLStreamReader reader = factory.createXMLStreamReader(new ByteArrayInputStream(bytes));
            BpmnModel model = new BpmnXMLConverter().convertToBpmnModel(reader);
            deployment = repositoryService.createDeployment().addBpmnModel(file.getOriginalFilename(), model).deploy();
        } else {
            throw new IllegalArgumentException("不支持的文件类型");
        }
        // 忽略二进制文件（模板文件、模板图片）返回
        DeploymentVo deploymentVO = BeanUtils.copyBean(deployment, DeploymentVo.class, "resources");

        // 根据部署ID查询流程定义
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        processDefinitionQuery.deploymentId(deployment.getId());
        ProcessDefinition processDefinition = processDefinitionQuery.singleResult();

        if (processDefinition == null) {
            return deploymentVO;
        }

        String processDefinitionId = processDefinition.getId();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        byte[] xmlBytes = new BpmnXMLConverter().convertToXML(bpmnModel);
        String xml = new String(xmlBytes, StandardCharsets.UTF_8);
        String key = processDefinition.getKey();

        BizFlow bizFlow = bizFlowService.query().andEq(BizFlow::getFlowCode, key).andEq(BizFlow::getFlowVersion, 0).single();
        if (bizFlow != null) {
            bizFlow.setFlowModel(xml);
            bizFlowService.updateById(bizFlow);
        }

        return deploymentVO;
    }

    public DeploymentVo publish(String id) {
        // 获取流程模型
        BizFlow bizFlow = bizFlowService.getById(id);
        if (ObjectUtil.isNull(bizFlow) || StringUtil.isBlank(bizFlow.getFlowModel())) {
            throw new RuntimeException("流程模型不存在或设计不完整！");
        }
        // 获取流程图
        String bpmnXml = bizFlow.getFlowModel();
        String processName = bizFlow.getFlowName() + ProcessConstant.SUFFIX;
        byte[] bpmnBytes = bpmnXml.getBytes(StandardCharsets.UTF_8);

        // 部署流程
        Deployment deployment = repositoryService.createDeployment().name(bizFlow.getFlowName()).key(bizFlow.getFlowCode()).category("").addBytes(processName, bpmnBytes).deploy();

        // 修改流程定义的分类，便于搜索流程
        ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
        List<FlowAct> flowActList = flowActService.getSQLManager().lambdaQuery(FlowAct.class).andEq(FlowAct::getFlowId, bizFlow.getId()).select();

        // 创建Flow的新配置
        bizFlow.setId(null);
        bizFlow.setFlowVersion(procDef.getVersion());
        bizFlowService.save(bizFlow);

        // 创建Act的新配置
        for (FlowAct flowAct : flowActList) {
            flowAct.setId(null);
            flowAct.setFlowVersion(procDef.getVersion());
            flowAct.setFlowId(bizFlow.getId());
        }
        flowActService.saveBatch(flowActList);

        // 忽略二进制文件（模板文件、模板图片）返回
        DeploymentVo deploymentVO = BeanUtils.copyBean(deployment, DeploymentVo.class, "resources");
        return deploymentVO;
    }

    public PageResult<DeployVo> queryPageList(PageQuery<DeployVo, DeployVoQuery> pageQuery) throws ParseException {
        // 流程定义列表数据查询
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery().latestVersion().orderByProcessDefinitionKey().asc();
        // 构建搜索条件
        ProcessUtil.buildProcessSearch(processDefinitionQuery, pageQuery.getSimpleCondition());
        long pageTotal = processDefinitionQuery.count();
        if (pageTotal <= 0) {
            return pageQuery.of(null, 0l);
        }
        long offset = pageQuery.getPageSize() * (pageQuery.getPageNo() - 1);
        List<ProcessDefinition> definitionList = processDefinitionQuery.listPage((int) offset, pageQuery.getPageSize());

        List<DeployVo> deployVoList = new ArrayList<>(definitionList.size());
        for (ProcessDefinition processDefinition : definitionList) {
            String deploymentId = processDefinition.getDeploymentId();
            Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
            DeployVo vo = new DeployVo();
            vo.setDefinitionId(processDefinition.getId());
            vo.setProcessKey(processDefinition.getKey());
            vo.setProcessName(processDefinition.getName());
            vo.setVersion(processDefinition.getVersion());
            vo.setCategory(processDefinition.getCategory());
            vo.setDeploymentId(processDefinition.getDeploymentId());
            vo.setSuspended(processDefinition.isSuspended());
            // 流程部署信息
            vo.setCategory(deployment.getCategory());
            vo.setDeploymentTime(deployment.getDeploymentTime());
            deployVoList.add(vo);
        }

        PageResult<DeployVo> page = pageQuery.of(deployVoList, pageTotal);
        return page;
    }

    public PageResult<DeployVo> queryPublishList(PageQuery<DeployVo, DeployVoQuery> pageQuery) {
        // 创建查询条件
        DeployVoQuery deployVoQuery = pageQuery.getSimpleCondition();
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery().processDefinitionKey(deployVoQuery.getQueryProcessKey()).orderByProcessDefinitionVersion().desc();
        long pageTotal = processDefinitionQuery.count();
        if (pageTotal <= 0) {
            return pageQuery.of(null, 0l);
        }
        // 根据查询条件，查询所有版本
        long offset = pageQuery.getPageSize() * (pageQuery.getPageNo() - 1);
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.listPage((int) offset, pageQuery.getPageSize());
        List<DeployVo> deployVoList = processDefinitionList.stream().map(item -> {
            DeployVo vo = new DeployVo();
            vo.setDefinitionId(item.getId());
            vo.setProcessKey(item.getKey());
            vo.setProcessName(item.getName());
            vo.setVersion(item.getVersion());
            vo.setCategory(item.getCategory());
            vo.setDeploymentId(item.getDeploymentId());
            vo.setSuspended(item.isSuspended());
            return vo;
        }).collect(Collectors.toList());

        PageResult<DeployVo> page = pageQuery.of(deployVoList, pageTotal);
        return page;
    }

    public String queryBpmnXmlById(String definitionId) {
        InputStream inputStream = repositoryService.getProcessModel(definitionId);
        try {
            return IoUtil.readUtf8(inputStream);
        } catch (IORuntimeException exception) {
            throw new RuntimeException("加载xml文件异常");
        }
    }
}
